package com.polaris.live.utils.zego.core

import com.polaris.live.common.cacha.CacheUtil
import com.polaris.live.common.manager.withRetryNow
import com.polaris.live.common.network.getSuccessResult
import com.polaris.live.net.liveService
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.TimeUnit

/**
 * token管理器，内部会维护token，token过期后会自动获取新的token
 *
 * @author Created by lucas on 2023/11/25 19:53
 * @since 1.0.0
 */
class ZegoTokenHandler {

    private val tokenMutex = Mutex()

    @Volatile
    private var currentToken: String? = null
    private var lastTimestamp: Long? = null

    /**
     * 获取token
     */
    suspend fun getToken(zegoErrorCode: Int?): String {
        val token = currentToken

        if (token != null && zegoErrorCode == null) {
            val lastTimestamp = lastTimestamp
            if (lastTimestamp != null && CacheUtil.getServerTime() - lastTimestamp < TimeUnit.HOURS.toMillis(12)) {
                return token
            }
        }

        return tokenMutex.withLock {
            //获取锁后已经存在说明一定是新的，直接返回
            val afterToken = currentToken
            if (afterToken != null && afterToken != token) {
                return afterToken
            }
            //先将之前的token重置为空，使得后面的请求都会等待
            currentToken = null

            withRetryNow {
                val resp = liveService.getRtcToken().getSuccessResult()
                    ?: throw IllegalArgumentException()

                resp.token.apply {
                    currentToken = this
                    lastTimestamp = CacheUtil.getServerTime()
                }
            }
        }
    }

    /**
     * 返回缓存的token，日志用
     */
    fun getCacheToken(): String? {
        return currentToken
    }

    /**
     * 这里检查一个token是否有效，这里只按照拦截器的鉴权失败处理
     */
    suspend fun checkToken() {
        val resp = liveService.getRtcToken().getSuccessResult()
        if (resp != null) {
            currentToken = resp.token
        }
    }

    /**
     * 释放token，一般在退出登录时调用
     *
     * 这里要保证在调用之后不要调用[getToken]，否则token会重新生成
     */
    fun releaseToken() {
        currentToken = null
    }
}